package com.clusterswisdom.horn.api.event;

import java.io.IOException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;

import com.alibaba.fastjson.JSONObject;
import com.clusterswisdom.horn.api.db.DbUtil;
import com.clusterswisdom.horn.api.db.SaveLog;
import com.clusterswisdom.horn.api.ioc.HornAction;
import com.clusterswisdom.horn.api.ioc.HornResponse;
import com.clusterswisdom.horn.api.ioc.api.ApiConfig;
import com.clusterswisdom.horn.api.ioc.api.HornConfig;
import com.clusterswisdom.horn.api.ioc.api.ResponseConfig;
import com.clusterswisdom.horn.api.ioc.api.ResponseField;
import com.clusterswisdom.horn.api.ioc.api.ResponseType;
import com.clusterswisdom.horn.api.ioc.struct.StructConfig;
import com.clusterswisdom.horn.api.ioc.struct.StructContainer;
import com.clusterswisdom.horn.api.ioc.struct.StructFormatter;
import com.clusterswisdom.horn.api.push.PushDataUtil;
import com.clusterswisdom.horn.api.util.ParamUtil;
import com.clusterswisdom.horn.api.util.ResponseUtil;

public class BaseProcessor {
	
	protected HornResponse process(ApiConfig apiConfig, JSONObject requestData) throws Exception {
		HornAction action = getAction(apiConfig);
		if (action == null) {
			throw new Exception("can not find api " + apiConfig.getService().getService() + "." + apiConfig.getName());
		} 
		
		JSONObject inputParam = formateParam(apiConfig, requestData);
		HornResponse response = action.execute(apiConfig, inputParam);
		
		if (response.isSuccess()) {
			response = formateResponse(apiConfig, inputParam, response);
			if (apiConfig.getResponse().isSaveData()) {
				saveResponseData(apiConfig, response.getData());
			}
		}
		
		return response;
	}
	
	protected Object processResponse(ResponseConfig config, HornResponse response) throws Exception {
		if (config.getProcessor() != null) {
			return config.getProcessor().process(config, response);
			
		} else {
			ResponseType type = config.getType();
			if (type == null) {
				return null;
				
			} else if (ResponseType.STATUS.equals(type)) {
				Map<String, String> stateResponse = new HashMap<String, String>();
				stateResponse.put("code", response.getCode());
				stateResponse.put("message",  response.getMessage() == null ? "" :  response.getMessage());
				return stateResponse;
				 
			} else if (ResponseType.DATA.equals(type)) {
				if (response.getCode().equals(HornResponse.CODE_SUCCESS)) {
					return response;
					
				} else {
					Map<String, String> stateResponse = new HashMap<String, String>();
					stateResponse.put("code", response.getCode());
					stateResponse.put("message",  response.getMessage() == null ? "" :  response.getMessage());
					return stateResponse;
					
				}
				 
			} else if (ResponseType.PUSH.equals(type)) {
				return pushData(config, response);
				
			}
			
		}
		
		return null;
		
	}
	
	protected HornAction getAction(ApiConfig apiConfig) {
		HornAction action = apiConfig.getAction();
		if (action == null) {
			return HornConfig.getConfig().getDefualtAction(apiConfig.getService().getChannel());
			
		} else {
			return action;
			
		}
	}
	
	protected String pushData(ResponseConfig config, HornResponse response) throws IOException {
		int repeatCount = 0;
		boolean send = false;
		String result = null;
		
		while (!send && repeatCount < 3) {
			try {
				result = PushDataUtil.getUtil().pushData(config.getUrl(), response.getData());
				send = true;
				return result;
				
			} catch (IOException e) {
				repeatCount ++;
				if (repeatCount == 3) {
					throw e;
				}
				
			}
		}
		
		return result;
	}
	
	private HornResponse formateResponse(ApiConfig apiConfig, JSONObject param, HornResponse response) throws Exception {
		ResponseConfig responseConfig = apiConfig.getResponse();
		if (responseConfig.getFormater() != null) {
			response = responseConfig.getFormater().formate(apiConfig, param, response);
		}
		
		String channel = apiConfig.getService().getChannel();
		if (responseConfig.getResponseFields() != null && responseConfig.getResponseFields().size() > 0) {
			response.setData(getResponseFieldsData(channel, param, response, responseConfig.getResponseFields()));
		}
		
		if (responseConfig.getStruct() != null) {
			Object data = response.getData();
			if (!StringUtils.isEmpty(responseConfig.getProperty())) {
				data = ResponseUtil.getObjectProperty(channel, response.getData(), responseConfig.getProperty());
			}
			
			response.setData(parseResponseData(responseConfig.getStruct(), data));
		}
		
		if (responseConfig.getFields() != null && responseConfig.getFields().length > 0) {
			response.setData(getFieldsData(channel, response.getData(), responseConfig.getFields()));
		}
		
		return response;
	}
	
	protected void saveResponseData(ApiConfig apiConfig, Object response) throws SQLException {
		String struct = apiConfig.getResponse().getStruct();
		StructConfig structConfig = StructContainer.getContainer().getStruct(struct);
		if (structConfig == null) {
			return;
		}
		
		if (response instanceof JSONObject) {
			DbUtil.getUtil().megerStruct(structConfig, (JSONObject)response);
		}
	}
	
	protected void sendException(ApiConfig apiConfig, Long logId, Exception e) {
		if (logId != null) {
			String msg = e.getCause().getMessage() + ":" + e.getMessage(); 
			DbUtil.getUtil().logResult(logId, SaveLog.TYPE_EXCEPTION, msg);
		}
		
		//推送错误信息
		JSONObject exceptionMassage = new JSONObject();
		exceptionMassage.put("exType", "应用程序异常");
		exceptionMassage.put("exOid", "");
		exceptionMassage.put("exContent", e.getMessage());
		exceptionMassage.put("exFunc", "");
		try {
			PushDataUtil.getUtil().pushData("${pushServer}/tmall/exception/sendExceptionInfo.htm",exceptionMassage);
		} catch (IOException e1) {
			e1.printStackTrace();
		}
	}
	
	private JSONObject getResponseFieldsData(String channel, JSONObject param, HornResponse response, List<ResponseField> fields) {
		JSONObject responseValue = new JSONObject();
		for (ResponseField responseField : fields) {
			Object value = null;
			if (ResponseField.SOURCE_INPUT.equals(responseField.getSource())) {
				value = getSourcePropertyValue(channel, param, responseField.getProperty());
				
			} else {
				value = getSourcePropertyValue(channel, response.getData(), responseField.getProperty());
				
			}
			
			responseValue.put(responseField.getName(), ResponseUtil.formateValue(value, responseField));
		}
		
		return responseValue;
	}
	
	private JSONObject getFieldsData(String channel, Object source, String[] fields) {
		JSONObject responseValue = new JSONObject();
		for (String fieldName : fields) {
			responseValue.put(fieldName, getSourcePropertyValue(channel, source, fieldName));
		}
		
		return responseValue;
	}
	
	@SuppressWarnings("rawtypes")
	private Object getSourcePropertyValue(String channel, Object source, String property) {
		if (source instanceof Map) {
			return ((Map) source).get(property);
			
		} else {
			return ResponseUtil.getObjectProperty(channel, source, property);
			
		}
	}
	
	private JSONObject formateParam(ApiConfig config, JSONObject inputParam) throws Exception {
		JSONObject param = ParamUtil.formateInputParam(inputParam, config.getRequest().getParams());
		if (config.getRequest() != null && config.getRequest().getParser() != null) {
			return config.getRequest().getParser().getParam(config, param);
			
		} else {
			return param;
			
		}
	}
	
	private Object parseResponseData(String struct, Object data) throws Exception {
		StructConfig config = StructContainer.getContainer().getStruct(struct);
		StructFormatter formatter = getFormatter(config.getFormate());
		if (formatter == null) {
			return data;
			
		} else {
			return formatter.format(data, config);
			
		}
	}
	
	private StructFormatter getFormatter(String formate) {
		if (formate == null || formate.trim().length() == 0) {
			return null;
		}
		
		try {
			return (StructFormatter)Thread.currentThread().getContextClassLoader().loadClass(formate).newInstance();
			
		} catch (Exception e) {
			e.printStackTrace();
			return null;
			
		}
	}
}